package com.dopplr.examples
{
    import com.dopplr.api.API;
    import com.dopplr.api.City;
    import com.dopplr.api.Traveller;
    import com.dopplr.api.Trip;
    import com.modestmaps.extras.Distance;
    import com.modestmaps.geo.Location;
    
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.ErrorEvent;
    import flash.events.StatusEvent;
    import flash.text.TextField;
    import flash.utils.Dictionary;

    [SWF(backgroundColor='#ffffff')]
    public class DopplrArcs extends Sprite
    {
		[Embed(systemFont="Helvetica Neue", fontName="HelveticaNorm", mimeType='application/x-font')]
		public var _helv:String;
		[Embed(systemFont="Helvetica Neue", fontName="HelveticaBold", fontWeight="bold", mimeType='application/x-font')]
		public var _helvbo:String;        
        
        public var api:API;
        
        public var me:Traveller;
        public var myLocationsByDay:Dictionary; // Date --> City
        
        public var fellows:Array;
        public var fellowLocationsByDay:Dictionary; // User --> Dictionary(Date --> City)

        // time helpers
        public var todayTemp:Date;
        public var today:Date;
        public var millisInADay:Number;
        public var millisIn3Months:Number;
        public var future:Function;
        public var days:Array; // days we're interested in
        
        public function DopplrArcs()
        {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            
            initTimeHelpers();

            // the API token is passed into Flash via flashvars in the HTML
            // to get this working you can hardcode one from http://www.dopplr.com/account/api_session_token
            // but don't distribute it in code or a swf!
            var token:String = LoaderInfo(root.loaderInfo).parameters.token;
            if (token) {
                api = new API(token);
                api.addEventListener(ErrorEvent.ERROR, onAPIError);
                api.addEventListener(StatusEvent.STATUS, onAPIStatus);
                
                // travellerLoaded is a function, below, which accepts a com.dopplr.api.Traveller instance
                api.loadAuthenticatedUser(travellerLoaded);                
            }
            else {
                var text:TextField = new TextField();
                text.name = 'error';
                text.text = "problem initializing dopplr access: no token found";
                text.width = text.textWidth + 4;
                addChild(text);
            }
            
        }
        
        public function initTimeHelpers():void
        {
            todayTemp = new Date();
            today = new Date(Date.UTC(todayTemp.fullYearUTC, todayTemp.monthUTC, todayTemp.dateUTC));
            millisInADay = 24 * 60 * 60 * 1000;
            millisIn3Months = 90 * millisInADay;
            future = function(item:Trip, index:int, array:Array):Boolean
            {
                return item.finish.time > today.time && item.start.time < today.time + millisIn3Months;
            };
            days = [];
            for (var t:Number = today.time; t < today.time + millisIn3Months; t += millisInADay) {
                days.push(new Date(t));
            }
        }
        
        public function travellerLoaded(user:Traveller):void
        {
            api.loadTravellerTrips(user, gotMe);
        }
        
        public function gotMe(user:Traveller):void
        {
            me = user;
            myLocationsByDay = getDailyLocations(user);
            
/*             for (var key:Object in myLocationsByDay) {
                var day:Date = key as Date;
                var city:City = myLocationsByDay[day] as City;
                trace(day.toUTCString() + " --> " + city);
            } */

            api.loadFellowTravellers(gotFellows);            
        }
        
        public function gotFellows(users:Array):void
        {
            api.loadFellowTravellersTrips(users, gotFellowTrips);
        }
        
        public function gotFellowTrips(users:Array):void
        {
            api.loadFellowTravellersInfo(users, gotFellowInfo);
        }
        
        public function gotFellowInfo(users:Array):void
        {
            fellows = users;
            fellowLocationsByDay = new Dictionary();
            for each (var user:Traveller in users) {
                fellowLocationsByDay[user] = getDailyLocations(user);
            }
            drawArcs();
        }
                
        public function getDailyLocations(user:Traveller):Dictionary
        {
            var dailyLocations:Dictionary = new Dictionary();
            
            var day:Date;            
            // initialize daily location to home city
            for each (day in days) {
                dailyLocations[day] = user.homeCity;
            }
            
            // look at future trips and override daily location for duration of trips
            // don't worry about order or overlapping trips for now
            var trips:Array = user.trips.filter(future);
            trace(user.name + " will be... ");
            for each (var trip:Trip in trips) {
                var tripDays:Array = days.filter(function(item:Date, index:int, array:Array):Boolean {
                    return item.time >= trip.start.time && item.time <= trip.finish.time;
                }); // not optimal, but gives us an array with the same objects we used in the home city loop, above
                for each (day in tripDays) {
                    dailyLocations[day] = trip.city;
                    trace("in " + dailyLocations[day] + " on " + day.toUTCString());
                }
            }    
            
            return dailyLocations;
        }
        
        public function drawArcs():void
        {
            var userDistances:Dictionary = new Dictionary();
            var user:Traveller;
            var day:Date;
            var minDistance:Number = Number.MAX_VALUE;
            var maxDistance:Number = Number.MIN_VALUE;
            var distances:Dictionary;
            var myCity:City;
            var theirCity:City;
            var locationsByDay:Dictionary;
            for each (user in fellows) {
                distances = new Dictionary();
                userDistances[user] = distances;
                locationsByDay = fellowLocationsByDay[user] as Dictionary;
                for each (day in days) {
                    myCity = myLocationsByDay[day] as City;
                    theirCity = locationsByDay[day] as City;
                    var myLocation:Location = new Location(myCity.latitude, myCity.longitude);
                    var theirLocation:Location = new Location(theirCity.latitude, theirCity.longitude);
                    var distance:Number = Distance.approxDistance(myLocation, theirLocation);
                    minDistance = Math.min(distance, minDistance);
                    maxDistance = Math.max(distance, maxDistance);
                    distances[day] = distance;
                }
            }
            var cx:Number = stage.stageWidth / 2;
            var cy:Number = stage.stageHeight / 2;
            var maxR:Number = Math.min(cx, cy) * 0.9;
            var minR:Number = maxR / 50;
            for each (user in fellows) {
                var s:Sprite = new Sprite();
                addChild(s);
                s.alpha = 0.5;
                //trace(user.name);
                distances = userDistances[user] as Dictionary;
                locationsByDay = fellowLocationsByDay[user] as Dictionary;
                var a:Number = 0;
                var moved:Boolean = false;
                var prevCity:City;
                for each (day in days) {
                    var d:Number = distances[day] as Number;
                    //trace("\t" + day.toUTCString() + " ---> " + d.toString());
                    var prop:Number = (d - minDistance) / (maxDistance - minDistance);
                    var r:Number = minR + prop * (maxR-minR);
                    var px:Number = cx + Math.cos(a) * r;
                    var py:Number = cy + Math.sin(a) * r;
                    theirCity = locationsByDay[day] as City;
                    if (moved) {
                        s.graphics.lineStyle(1, theirCity.rgb, 1, false);
                        //s.graphics.lineGradientStyle(GradientType.LINEAR, [prevCity.rgb, theirCity.rgb], [1,1], [0,255]);
                        s.graphics.lineTo(px,py);
                    }
                    else {
                        s.graphics.lineStyle(1, theirCity.rgb, 1, false);
                        s.graphics.moveTo(px, py);
                        moved = true;
                    }
                    a += Math.PI * 2 / (days.length-1);
                    prevCity = theirCity;
                }
            }
            onAPIStatus(new StatusEvent(StatusEvent.STATUS, false, false, 'fellow traveller distance arcs for ' + me.name, 'status')); 
        }

        /** add a textfield with an error, reuse the text field if possible */
        public function onAPIError(error:ErrorEvent):void
        {
            var text:TextField = this.getChildByName('error') as TextField;
            if (!text) {
                text = new TextField();
                text.name = 'error';
                text.textColor = 0x000000;
                text.background = true;
                text.backgroundColor = 0xffff00;
                addChild(text);                
            }
            text.text = "Error: " + error.text;
            text.width = text.textWidth + 4;
            text.height = text.textHeight + 4;
        }
        
        /** add a textfield with a status, reuse the text field if possible */
        public function onAPIStatus(event:StatusEvent):void
        {
            var status:TextField = this.getChildByName('status') as TextField;
            if (!status) {
                status = new TextField();
                status.name = 'status';
                status.textColor = 0x000000;
                status.background = true;
                status.backgroundColor = 0xffffff;
                addChild(status);                
            }
            status.text = event.code;
            status.width = status.textWidth + 4;
            status.height = status.textHeight + 4;
            status.x = stage.stageWidth - status.width - 15
            status.y = stage.stageHeight - status.height - 15;
        }

        
    }
}